package com.damai.pay.alipay;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.citywithincity.pay.OrderInfo;
import com.citywithincity.pay.PayException;
import com.citywithincity.pay.PayStatus;
import com.citywithincity.utils.HttpUtil;
import com.citywithincity.utils.IoUtil;
import com.citywithincity.utils.MD5Util;
import com.citywithincity.utils.MapUtil;
import com.damai.pay.PayInfo;
import com.damai.xml.XMLParseException;

public class AliPayModel {
	private static final String QUERY_URL = "https://mapi.alipay.com/gateway.do?service=single_trade_query&sign=";

	private static final Log logger = LogFactory.getLog(AliPayModel.class);

	private static final String ENCODING = "utf-8";

	// 安全教研吗
	private String safeKey;
	private String partner;
	private String seller;

	private String privateKey;

	public AliPayModel(String partner, String seller, String privateKey, String safeKey) {
		this.partner = partner;
		this.seller = seller;
		this.privateKey = privateKey;
		this.safeKey = safeKey;
	}

	/**
	 * 预支付
	 * 
	 * @param outId 商户id
	 * @param title  订单名称
	 * @param fee 订单金额（单位元）
	 * @param notifyUrl
	 * @return
	 * @throws PayException
	 * @throws IOException
	 */
	public String prePay(String outId, String title, double fee, String notifyUrl) throws PayException, IOException {
		StringBuilder sb = new StringBuilder(512);
		getOrderInfo(sb, title, fee, outId, notifyUrl);
		return getFinalString(sb);
	}

	/**
	 * 从服务器后台通知中解析订单信息
	 * @param request
	 * @return
	 * @throws PayException
	 * @throws UnsupportedEncodingException
	 */
	public OrderInfo parseOrder(HttpServletRequest request) throws PayException, UnsupportedEncodingException {
		// 验证有效性
		Map<String, String> params = new HashMap<String, String>();
		Map<String, String[]> requestParams = request.getParameterMap();
		for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
			String name = (String) iter.next();
			String[] values = (String[]) requestParams.get(name);
			String valueStr = "";
			for (int i = 0; i < values.length; i++) {
				valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
			}
			// 乱码解决，这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
			// valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
			params.put(name, valueStr);
		}

		// 获取支付宝的通知返回参数，可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
		// 商户订单号
		String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
		// 支付宝交易号

		String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
		// 交易状态
		String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");

		if (AlipayNotify.verify(params, partner)) {

			if (trade_status.equals("TRADE_FINISHED") || trade_status.equals("TRADE_SUCCESS")) {
				OrderInfo orderInfo = new OrderInfo();
				orderInfo.setOrderId(out_trade_no);
				orderInfo.setPlatId(trade_no);
				orderInfo.setPayStatus(PayStatus.PayStatus_Success);
				return orderInfo;
			}
		}
		return null;
	}
	
	/**
	 * 验证前端通知的订单合法性
	 * @param id
	 * @param src
	 * @return
	 * @throws PayException
	 * @throws IOException
	 */
	public PayStatus queryClientNotifyOrderStatus(String id, String src) throws PayException, IOException {
		src = URLDecoder.decode(src, "UTF-8");
		byte[] bytes = Base64.decodeBase64(src);
		String alipayInfo = new String(bytes, "UTF-8");
		String target = alipayInfo.substring(0, alipayInfo.indexOf("&sign_type="));
		String sign = alipayInfo.substring(alipayInfo.indexOf(SIGN) + SIGN.length() + 1);
		sign = sign.substring(0, sign.length() - 1);
		// 这里验证
		if (AlipayNotify.verify(target, sign)) {
			String outId = getPart(alipayInfo, "&out_trade_no=");
			if (id.equals(outId)) {
				return PayStatus.PayStatus_Success;
			}
			throw new PayException("订单id与信息中的订单id不一致", null);
		}
		throw new PayException("签名错误", null);
	}
	
	public PayInfo parse(String id, String src){
		try{
			src = URLDecoder.decode(src, "UTF-8");
			byte[] bytes = Base64.decodeBase64(src);
			String alipayInfo = new String(bytes, "UTF-8");
			String target = alipayInfo.substring(0, alipayInfo.indexOf("&sign_type="));
			String sign = alipayInfo.substring(alipayInfo.indexOf(SIGN) + SIGN.length() + 1);
			sign = sign.substring(0, sign.length() - 1);
			// 这里验证
			if (AlipayNotify.verify(target, sign)) {
				String outId = getPart(alipayInfo, "&out_trade_no=");
				if (id.equals(outId)) {
					
				
					return new AliPayInfoClient(outId);
					
				}
				throw new PayException("订单id与信息中的订单id不一致", null);
			}
			throw new PayException("签名错误", null);
		}catch(IOException e){
			throw new PayException("编码不支持", null);
		}
		
		
	}
	
	
	public static class AliPayInfoClient implements PayInfo{
		private String outId;
		public AliPayInfoClient(String outId){
			this.outId = outId;
		}

		@Override
		public boolean isPaySuccess() {
			return true;
		}

		@Override
		public boolean isRefund() {
			// TODO Auto-generated method stub
			return false;
		}

		@Override
		public int getFee() {
			// TODO Auto-generated method stub
			return 0;
		}

		@Override
		public int getRealFee() {
			// TODO Auto-generated method stub
			return 0;
		}

		@Override
		public int getType() {
			// TODO Auto-generated method stub
			return 0;
		}

		@Override
		public String getOutId() {
			return outId;
		}

		@Override
		public String getPlatId() {
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public Date getDate() {
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public Object getExtra() {
			// TODO Auto-generated method stub
			return null;
		}
		
	}

	private String getFinalString(StringBuilder sb) throws PayException {
		// 对订单做RSA 签名
		String sign = sign(sb.toString());
		try {
			// 仅需对sign 做URL编码
			sign = URLEncoder.encode(sign, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw new PayException(e.getMessage(), "UTF8Encoding");
		}
		sb.append("&sign=\"").append(sign).append("\"&sign_type=\"RSA\"");
		return sb.toString();
	}

	private String sign(String content) {
		return RSA.sign(content, privateKey);
	}

	
	/**
	 * 
	 * 暂时不支持
	 * 
	 * @param outId							商户订单号
	 * @return
	 * @throws PayException
	 * @throws IOException
	 */
	public Map<String, Object> queryOrder(String outId) throws PayException, IOException {

		Map<String, String> data = new TreeMap<String, String>();
		data.put("service", "single_trade_query");
		data.put("partner", partner);
		data.put("_input_charset", ENCODING);
		data.put("out_trade_no", outId);
		StringBuilder sb = new StringBuilder(200);
		boolean first = true;
		for (Entry<String, String> entry : data.entrySet()) {
			if (first) {
				first = false;
			} else {
				sb.append('&');
			}
			sb.append(entry.getKey()).append('=').append(entry.getValue());
		}
		String sign = MD5Util.md5Appkey(sb.append(safeKey).toString());
		sb.setLength(0);
		sb.append(QUERY_URL)
			.append(sign)
			.append("&out_trade_no=")
			.append(outId)
			.append("&partner=")
			.append(partner)
			.append("&sign_type=MD5&_input_charset=utf-8");
		// String url = URLEncoder.encode(sb.toString(), "UTF-8");
		HttpURLConnection connection = null;
		InputStream inputStream = null;
		try {
			logger.info(sb.toString());
			connection = createConnection(sb.toString());
			inputStream = new BufferedInputStream(connection.getInputStream());
			
			String content = IoUtil.readContent(inputStream, "UTF-8");
			logger.info(content);
			
			Map<String, Object> xml = parseXML(new ByteArrayInputStream(content.getBytes()));
			logger.info(MapUtil.toString(xml));

			
			return xml;
		} catch (XMLParseException e) {
			throw new IOException(e);
		} finally {
			IoUtil.close(inputStream);
			if (connection != null)
				connection.disconnect();
		}
	}
	
	private static class AlipayInfo{
		
	}

	/**
	 * 
	 * @param is
	 * @param isTree
	 * @param encoding
	 * @return
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 */
	public static Map<String, Object> parseXML(InputStream is)
			throws XMLParseException, IOException {
		try {
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();
			Document document = db.parse(is, "UTF-8");
			Map<String, Object> map = new HashMap<String, Object>();
			NodeList nodes = document.getChildNodes().item(0).getChildNodes();
			for (int i = 0, count = nodes.getLength(); i < count; i++) {
				Node node = nodes.item(i);
				if (node instanceof Element) {
					String name = node.getNodeName();
					if("request".equals(name)){
						map.put("request", parseRequest(node));
					}else if("response".equals(name)){
						parseResponse(map,node.getFirstChild());
					}else{
						map.put(node.getNodeName(), node.getTextContent());
					}
					
				}
			}
			return map;
		} catch (ParserConfigurationException e) {
			throw new XMLParseException(e);
		} catch (SAXException e) {
			throw new XMLParseException(e);
		}

	}

	private static Map<String, Object> parseResponse(Map<String, Object> result,Node parent) {

		NodeList nodes =  parent.getChildNodes();
		for (int i = 0, count = nodes.getLength(); i < count; i++) {
			Node node = nodes.item(i);
			if (node instanceof Element) {
				result.put(node.getNodeName(), node.getTextContent());
			}
		}
		return result;
	}

	private static String parseRequest(Node node) {
		// TODO Auto-generated method stub
		return null;
	}

	private static HttpURLConnection createConnection(String url) throws IOException {
		URL postUrl = new URL(url);
		HttpURLConnection connection = (HttpURLConnection) postUrl.openConnection();
		connection.setDoOutput(true);
		connection.setDoInput(true);
		connection.setRequestMethod("GET");
		connection.setUseCaches(false);
		connection.setInstanceFollowRedirects(true);
		connection.setChunkedStreamingMode(5);
		connection.setConnectTimeout(HttpUtil.CONNECT_TIMEOUT);
		connection.setReadTimeout(HttpUtil.READ_TIMEOUT);
		connection.connect();
		return connection;
	}

	/**
	 * create the order info. 创建订单信息
	 * 
	 */
	private void getOrderInfo(StringBuilder sb, String subject, double price, String outID, String notifyUrl) {
		// 签约合作者身份ID
		sb.append("partner=").append('\"').append(partner).append('\"')

				// 签约卖家支付宝账号
				.append("&seller_id=").append('\"').append(seller).append('\"')

				// 商户网站唯一订单号
				.append("&out_trade_no=").append('\"').append(outID).append('\"')

				// 商品名称
				.append("&subject=").append('\"').append(subject).append('\"')

				// 商品详情
				// .append( "&body=" ).append( '\"' ).append( body ).append(
				// '\"')

				// 商品金额
				.append("&total_fee=").append('\"').append(price).append('\"')

				// 服务器异步通知页面路径
				.append("&notify_url=").append('\"').append(notifyUrl).append('\"')

				// 服务接口名称， 固定值
				.append("&service=\"mobile.securitypay.pay\"")

				// 支付类型， 固定值
				.append("&payment_type=\"1\"")

				// 参数编码， 固定值
				.append("&_input_charset=\"utf-8\"")

				// 设置未付款交易的超时时间
				// 默认30分钟，一旦超时，该笔交易就会自动被关闭。
				// 取值范围：1m～15d。
				// m-分钟，h-小时，d-天，1c-当天（无论交易何时创建，都在0点关闭）。
				// 该参数数值不接受小数点，如1.5h，可转换为90m。
				.append("&it_b_pay=\"30m\"")

				// extern_token为经过快登授权获取到的alipay_open_id,带上此参数用户将使用授权的账户进行支付
				// .append( "&extern_token=" ).append( '\"' ).append(
				// extern_token ).append( '\"';

				// 支付宝处理完请求后，当前页面跳转到商户指定页面的路径，可空
				.append("&return_url=\"m.alipay.com\"");

		// 调用银行卡支付，需配置此参数，参与签名， 固定值 （需要签约《无线银行卡快捷支付》才能使用）
		// .append( "&paymethod=\"expressGateway\"";

	}

	private static final String SIGN = "&sign=";

	

	private static String getPart(String info, String part) {
		String sign = info.substring(info.indexOf(part) + part.length() + 1);
		sign = sign.substring(0, sign.indexOf("\""));
		return sign;
	}
}
